//
//  LVDownLoadManager.m
//  Live
//
//  Created by 唐开福 on 16/8/8.
//  Copyright © 2016年 Heller. All rights reserved.
//

#import "LVDownLoadManager.h"
#import <YYKit/YYKit.h>
#import "BNDConstance.h"
#import "BNDPreference.h"
#import "ReminderHUD.h"
#import "Masonry.h"
#import "UIButton+Convenient.h"
#import "UIView+YYAdd.h"
#import <ReactiveObjC/ReactiveObjC.h>
#import "BNDApiManager.h"
#import "BNDPushVCHandler.h"
#import "BNDCommonMarcos.h"
//#import "SSZipArchive.h"
@implementation LVDownLoadInfo


- (unsigned long long)totalBytesWritten{
    
    unsigned long long fileSize =  [[[NSFileManager defaultManager] attributesOfItemAtPath:self.downLoadingPath error:nil] fileSize];
    return fileSize;
}
#pragma mark - <NSCoding>
- (void)encodeWithCoder:(NSCoder *)aCoder{
    [self modelEncodeWithCoder:aCoder];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
     self = [super init];
    return [self modelInitWithCoder:aDecoder];
}
@end

@interface LVDownLoadManager() <NSURLSessionDataDelegate>

@property (strong, nonatomic) NSURLSession *session;
@property (copy  , nonatomic) NSString *downloadingPath; /**< 下载时候暂存的路径*/
@property (copy  , nonatomic) NSString *yyCachePath; /**< 音乐和歌词存放的路径*/
@property (copy  , nonatomic) NSString *name; /**< 音乐和歌词存放的路径*/

@end

static id g_sharedInstance = nil;


static NSMutableDictionary *_managerDictinary;


@implementation LVDownLoadManager

+ (instancetype)managerWithName:(NSString *)name{
    if(_managerDictinary == nil){
        _managerDictinary = [NSMutableDictionary dictionary];
    }
    if(_managerDictinary[name] == nil){
       LVDownLoadManager *manager =  [[self alloc] initWithName:name];
        [_managerDictinary setObject:manager forKey:name];
        manager.name = name;
    }
    return _managerDictinary[name];
}

#pragma 快捷创建方法



- (instancetype)initWithName:(NSString *)name{
    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    return [self initWithFilePath:[cachePath stringByAppendingPathComponent:name]];
}


- (instancetype)initWithFilePath:(NSString *)filePath{
    self = [super init];
    if(self){
        
        self.maxOperationCount = 2;
        self.cachePath = filePath;        
        if (![[NSFileManager defaultManager] fileExistsAtPath:self.cachePath]) {
            [[NSFileManager defaultManager] createDirectoryAtPath:self.cachePath withIntermediateDirectories:YES attributes:nil error:nil];
        }
        self.downloadingPath = [self.cachePath stringByAppendingPathComponent:@"Downloading"];
        if(![[NSFileManager defaultManager] fileExistsAtPath:self.downloadingPath]){
           [[NSFileManager defaultManager] createDirectoryAtPath:self.downloadingPath withIntermediateDirectories:YES attributes:nil error:nil];
        }
        self.yyCachePath = [self.cachePath stringByAppendingPathComponent:@"YYCache"];
        self.cacheManager = [[YYCache alloc] initWithPath:self.yyCachePath];
        [self.cacheManager.memoryCache setShouldRemoveAllObjectsOnMemoryWarning:NO];
        [self.cacheManager.memoryCache setShouldRemoveAllObjectsWhenEnteringBackground:NO];

    }
    return self;
}


#pragma mark - 任务状态处理


- (void)cancelAllIsManualCancel:(BOOL)manual{
    WS(weakSelf)
    [self.downloadingArray enumerateObjectsUsingBlock:^(LVDownLoadInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [weakSelf cancel:obj.url isManualCancel:manual];
    }];
    
    [self.waitArray enumerateObjectsUsingBlock:^(LVDownLoadInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [weakSelf cancel:obj.url isManualCancel:manual];
    }];
}

- (void)cancel:(NSString *)url isManualCancel:(BOOL)manual{

    
    LVDownLoadInfo *info = [self downloadInfoForURL:url];
    
    if(info.downloadtask == nil){ // 说明已经取消过啦
        return;
    }
    info.state = LVDownloadStateSuspened;

    info.manualCancel = manual;
    [info.downloadtask cancel];
    info.downloadtask = nil;
    [self.downloadingArray removeObject:info];
    [self.waitArray removeObject:info];
    [self saveOrUpdateDownloadInfo:info];
    [self resumeNextDownloadTask];
}


- (void)suspendAllIsManualCancel:(BOOL)manual{
    [self.downloadingArray enumerateObjectsUsingBlock:^(LVDownLoadInfo * _Nonnull info, NSUInteger idx, BOOL * _Nonnull stop) {
        if(info.downloadtask == nil){ // 说明已经取消过啦
            return;
        }
        info.state = LVDownloadStateSuspened;
        info.manualCancel = manual;
        [info.downloadtask cancel];
        info.downloadtask = nil;
        [self saveOrUpdateDownloadInfo:info];
    }];
}
//
- (void)suspend:(NSString *)url isManualCancel:(BOOL)manual{
//    LVDownLoadInfo *info = [self downloadInfoForURL:url];
//    info.manualCancel = manual;
//    // 这两句代码是为了修复任务没有在下载就暂停的bug
//    // 因为刚创建出来task, 如果supend之后就不能再resume啦
//    if(info.state == LVDownloadStateResumed){
//        [info.downloadtask cancel];
//        info.downloadtask = nil;
//    }
//    info.state = LVDownloadStateSuspened;
//    [self.downloadingArray removeObject:info];
//    [self.waitArray removeObject:info];
//    [self resumeNextDownloadTask];
//    [self saveOrUpdateDownloadInfo:info];
    
    [self cancel:url  isManualCancel:manual];
    
}


- (void)suspend:(NSString *)url{
    
    [self suspend:url isManualCancel:NO];


}
- (void)resumeAll{
    WS(weakSelf)
    [self.downloadingArray enumerateObjectsUsingBlock:^(LVDownLoadInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [weakSelf resume:obj.url];
    }];
    
}
- (void)resume:(NSString *)url{
    LVDownLoadInfo *info = [self downloadInfoForURL:url];
    if (info.state == LVDownloadStateCompleted || info.state == LVDownloadStatedownLoadCompleted || ![info.url hasPrefix:@"http"]){
        return;
    }
    if(info.downloadtask == nil){
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:info.url]];
        NSString *range = [NSString stringWithFormat:@"bytes=%llu-", info.totalBytesWritten];
        [request setValue:range forHTTPHeaderField:@"Range"];
        info.downloadtask = [self.session dataTaskWithRequest:request];
        info.downloadtask.taskDescription = url;
        
    }

    if((self.downloadingArray.count < self.maxOperationCount) && ![self.downloadingArray containsObject:info]){
        
        [self.downloadingArray addObject:info];
        info.state = LVDownloadStateResumed;
        [info.downloadtask resume];
        
        
    }else if(![self.waitArray containsObject:info]){
        info.state = LVDownloadStateWillResume;
        [self.waitArray addObject:info];

    }
    [self saveOrUpdateDownloadInfo:info];

}



#pragma mark - 创建任务的方法
/**
 *  获得某个文件的下载信息
 *
 *  @param url 文件的URL
 */
- (LVDownLoadInfo *)downloadInfoForURL:(NSString *)url{
    if(url == nil) return nil;
    LVDownLoadInfo * loadInfo = (LVDownLoadInfo *)[self.cacheManager objectForKey:[url md5String]];
    loadInfo.downLoadingPath = [self.downloadingPath stringByAppendingPathComponent:loadInfo.filename];
    // 这个是处理意外情况下例如app直接被杀死，没有保存到暂停状态
    if(loadInfo.downloadtask == nil){
    if(loadInfo.state == LVDownloadStateResumed  || loadInfo.state == LVDownloadStateWillResume ||  loadInfo.state == LVDownloadStatedownLoadCompleted )
            loadInfo.state = LVDownloadStateSuspened;
    }

    return loadInfo;
}
/**
 *  创建下载任务
 */
- (LVDownLoadInfo *)creatDownloadTask:(NSString *)url  progress:(LVDownloadProgressChangeBlock)progress state:(LVDownloadStateChangeBlock)state{

    return [self creatDownloadTask:url name:nil progress:progress state:state];
}

- (LVDownLoadInfo *)creatDownloadTask:(NSString *)url name:(NSString *)name progress:(LVDownloadProgressChangeBlock)progress state:(LVDownloadStateChangeBlock)state{
    if(![url hasPrefix:@"http"]){
        NSLog(@"下载信息出错");
        return nil;
    }

    
    LVDownLoadInfo * loadInfo = (LVDownLoadInfo *)[self.cacheManager objectForKey:[url md5String]];
    if (loadInfo == nil){
        loadInfo = [[LVDownLoadInfo alloc] init];
        loadInfo.url = url; // 设置url
        if(name.length ==0){
            loadInfo.filename = [loadInfo.url md5String];
        }else{
            loadInfo.filename = name;
        }
     
        loadInfo.progressChangeBlock = progress;
        loadInfo.stateChangeBlock = state;
    }
    loadInfo.downloadName = name;

    [self saveOrUpdateDownloadInfo:loadInfo];
    [self resume:loadInfo.url];
    
    return loadInfo;
}


- (LVDownloadState)stateForDownloadUrl:(NSString *)url{
    LVDownLoadInfo * loadInfo = (LVDownLoadInfo *)[self.cacheManager objectForKey:[url md5String]];
    return loadInfo.state;
}


//- (void)deleteDownloadInfo:(NSString *)url shoudDeletedownloadData:(BOOL)isDelete{
//    if(url == nil) return;
//    if (isDelete){
//        LVDownLoadInfo *info = [self downloadInfoForURL:url];
//        [[NSFileManager defaultManager] removeItemAtPath:info.musicPath error:nil];
//        [[NSFileManager defaultManager] removeItemAtPath:info.lyricPath error:nil];
//    }
//    [self.cacheManager removeObjectForKey:[url md5String]];
//
//}

#pragma mark - 辅助方法
- (void)saveOrUpdateDownloadInfo:(LVDownLoadInfo *)info{
    
    [self.cacheManager setObject:info forKey:[info.url md5String] ];

}

#pragma mark - <NSURLSessionDataDelegate>


- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{

    LVDownLoadInfo *info = [self downloadInfoForURL:dataTask.taskDescription];
    if([response.allHeaderFields[@"Content-Length"] integerValue] == 0 && info.totalBytesWritten != 0){ // 说明已经下载过啦
        dispatch_async(dispatch_get_main_queue(), ^{
            if(info.progressChangeBlock != nil){
                info.progressChangeBlock(info.totalBytesWritten, info.totalBytesExpectedToWrite);
            }
            if(info.stateChangeBlock != nil){
                info.stateChangeBlock(LVDownloadStatedownLoadCompleted, info.downLoadingPath, nil);
            }
        });

    }
    
    info.totalBytesExpectedToWrite = [response.allHeaderFields[@"Content-Length"] integerValue] + info.totalBytesWritten;
    if (info.downLoadingPath == nil ||  ![[NSFileManager defaultManager] fileExistsAtPath:info.downLoadingPath]) {
        NSError *error = nil;
        NSString *dir = [info.downLoadingPath stringByDeletingLastPathComponent];
        [[NSFileManager defaultManager] createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error];
    }
    info.stream = [NSOutputStream outputStreamToFileAtPath:info.downLoadingPath append:YES];
    [info.stream open];
    [self saveOrUpdateDownloadInfo:info];
    completionHandler(NSURLSessionResponseAllow);
    
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    
    LVDownLoadInfo *info = [self downloadInfoForURL:dataTask.taskDescription];
    
    NSInteger result = [info.stream write:data.bytes maxLength:data.length];
    if (result == -1) {
        info.error = info.stream.streamError;
        NSLog(@"磁盘内存不足");
        [self cancel:info.url isManualCancel:NO];
        dispatch_async(dispatch_get_main_queue(), ^{
            if(info.stateChangeBlock != nil){
                info.stateChangeBlock(LVDownloadStateSuspened, info.downLoadingPath, nil);
            }
        });
        
    }else{
        
        unsigned long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:info.downLoadingPath error:nil][NSFileSize] longLongValue];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            info.totalBytesWritten = fileSize;

            if(info.progressChangeBlock != nil){
                info.progressChangeBlock(info.totalBytesWritten, info.totalBytesExpectedToWrite);
            }
        });
    }
    
    [self saveOrUpdateDownloadInfo:info];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    
    LVDownLoadInfo *info = [self downloadInfoForURL:task.taskDescription];
    [info.stream close];
    if (error && info.manualCancel){ // 用户手动取消下载
        [self cancel:task.taskDescription isManualCancel:NO];
        info.state = LVDownloadStateSuspened;

    }else if(error){
        [self cancel:task.taskDescription isManualCancel:NO];
        info.state = LVDownloadStateSuspened;
        info.error = error;
        dispatch_async(dispatch_get_main_queue(), ^{

            if(info.stateChangeBlock != nil){
                info.stateChangeBlock(info.state, info.downLoadingPath, nil);
            }
        });

    }else{ // 下载完成
        info.stream = nil;
        info.state = LVDownloadStatedownLoadCompleted;

        dispatch_async(dispatch_get_main_queue(), ^{

            if(info.stateChangeBlock != nil){
                info.stateChangeBlock(info.state, info.downLoadingPath, nil);
            }
        });

    }

    info.downloadtask = nil;
    [self.downloadingArray removeObject:info];
    [self resumeNextDownloadTask];
    [self saveOrUpdateDownloadInfo:info];
    [self.cacheManager setObject:info forKey:[task.response.URL.absoluteString md5String]];
    if(self.downloadingArray.count == 0 && self.waitArray.count == 0){
       LVDownLoadManager *manager =  _managerDictinary[self.name];
        [_managerDictinary removeObjectForKey:manager.name];
        [manager.session finishTasksAndInvalidate];
        manager = nil;
    }
}

- (void)resumeNextDownloadTask{
    if(self.waitArray.count){
        LVDownLoadInfo *info = [self.waitArray firstObject];
        [self.waitArray removeObject:info];
        [self resume:info.url];
    }
}


#pragma mark - 属性

- (NSURLSession *)session
{
    if (!_session) {
        NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
        self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:nil];
    }
    return _session;
}
- (NSOperationQueue *)queue
{
    if (!_queue) {
        self.queue = [[NSOperationQueue alloc] init];
        self.queue.maxConcurrentOperationCount = 5;
    }
    return _queue;
}

- (NSMutableArray *)downloadingArray{
    if(_downloadingArray == nil){
        _downloadingArray= [NSMutableArray array];
    }
    return _downloadingArray;
}
- (NSMutableArray *)waitArray{
    if(_waitArray == nil){
        _waitArray= [NSMutableArray array];
    }
    return _waitArray;
}



@end
